package main

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"os"
	"path/filepath"
	"time"
	"zinx-tianjing-study/ziface"
	"zinx-tianjing-study/znet"
)

// 计算文件MD5，用于检查文件是否完整
func calculateFileMD5(filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", err
	}
	defer file.Close()

	hash := md5.New()
	if _, err := io.Copy(hash, file); err != nil {
		return "", err
	}

	return fmt.Sprintf("%x", hash.Sum(nil)), nil
}

// 上传文件
func uploadFile(conn net.Conn, filePath string) error {
	// 获取文件信息
	fileInfo, err := os.Stat(filePath)
	if err != nil {
		return err
	}

	// 计算文件MD5
	fileMD5, err := calculateFileMD5(filePath)
	if err != nil {
		return err
	}

	// 计算分块数量，向上取整
	chunkCount := (int(fileInfo.Size()) + ziface.ChunkSize - 1) / ziface.ChunkSize
	fmt.Println("分块数量:", chunkCount)

	// 准备元数据
	metadata := znet.FileMetadata{
		FileName:   filepath.Base(filePath),
		FileSize:   fileInfo.Size(),
		FileMD5:    fileMD5,
		ChunkCount: chunkCount,
	}

	// 发送开始上传请求
	dp := znet.GetDataPack()
	metadataBytes, _ := json.Marshal(metadata)
	startMsg, _ := dp.Pack(znet.NewMessage(ziface.MsgIDFileStartUpload, metadataBytes))
	conn.Write(startMsg)

	// 读取响应
	if err := waitForSuccess(conn, dp); err != nil {
		return err
	}

	// 打开文件
	file, err := os.Open(filePath)
	if err != nil {
		return err
	}
	defer file.Close()

	FileID := fmt.Sprintf("%x", md5.Sum([]byte(metadata.FileName+metadata.FileMD5)))

	// 分块上传
	buffer := make([]byte, ziface.ChunkSize)
	for i := 0; i < chunkCount; i++ {
		fmt.Println("上传第", i+1, "块")
		n, err := file.Read(buffer)
		if err != nil && err != io.EOF {
			return err
		}

		chunk := znet.FileChunk{
			FileID:     FileID, // 使用文件MD5作为文件ID
			ChunkIndex: i,
			ChunkData:  buffer[:n],
		}

		// 发送文件块
		chunkBytes, _ := json.Marshal(chunk)
		chunkMsg, _ := dp.Pack(znet.NewMessage(ziface.MsgIDFileChunkUpload, chunkBytes))
		conn.Write(chunkMsg)

		// 等待响应
		if err := waitForSuccess(conn, dp); err != nil {
			return err
		}

		fmt.Printf("上传进度: %.2f%%\n", float64(i+1)/float64(chunkCount)*100)
	}

	// 发送完成请求
	fileIDBytes, _ := json.Marshal(FileID)
	finishMsg, _ := dp.Pack(znet.NewMessage(ziface.MsgIDFileUploadFinish, fileIDBytes))
	conn.Write(finishMsg)

	// 等待最终响应
	return waitForSuccess(conn, dp)
}

func waitForSuccess(conn net.Conn, dp *znet.DataPack) error {
	headData := make([]byte, dp.GetHeadLen())
	_, err := io.ReadFull(conn, headData)
	if err != nil {
		return err
	}

	msg, err := dp.UnPack(headData)
	if err != nil {
		return err
	}

	if msg.GetDataLen() > 0 {
		data := make([]byte, msg.GetDataLen())
		_, err = io.ReadFull(conn, data)
		if err != nil {
			return err
		}

		if msg.GetMsgID() == ziface.MsgIDFileUploadError {
			var response struct {
				Error string `json:"error"`
			}
			json.Unmarshal(data, &response)
			return fmt.Errorf("upload error: %s", response.Error)
		}
	}

	return nil
}

func main() {
	fmt.Println("文件上传客户端启动...")
	time.Sleep(1 * time.Second)

	conn, err := net.Dial("tcp", "127.0.0.1:8999")
	if err != nil {
		fmt.Println("连接服务器失败:", err)
		return
	}
	defer conn.Close()

	// 上传文件
	filePath := "D:\\myproject\\zinx-learn\\demo\\test.txt" // 替换为要上传的文件路径
	err = uploadFile(conn, filePath)
	if err != nil {
		fmt.Println("文件上传失败:", err)
		return
	}

	fmt.Println("文件上传成功!")
}
